/*
 * Copyright (c) 2025 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __HMFS_IO_H__
#define __HMFS_IO_H__

#include <memory>
#include "hmfs_command.h"
#include "hmfs_define.h"

#ifndef HAVE_LSEEK64
typedef off_t off64_t;
#endif

namespace OHOS::Hmfs {
/* ---------- dev_cache, Least Used First (LUF) policy  ------------------- */
/*
 * Least used block will be the first victim to be replaced when max hash
 * collision exceeds
 */
class Dcache {
public:
    static Dcache& GetInstance();
    static void DcacheInit();
    static void DcacheRelease();

    int DcacheUpdateCache(int fd, void *buf, off64_t offset, size_t count);
    int DcacheRead(int fd, void *buf, off64_t offset, size_t count);

private:
    Dcache() = default;

    static void DcachePrintStatistics();
    inline char *DcacheAddr(long entry);
    inline long DcacheRelocate(long entry, int n);
    long DcacheFind(off64_t blk);
    int DcacheIoRead(int fd, long entry, off64_t offset, off64_t blk);
    int DcacheUpdateRw(int fd, void *buf, off64_t offset, size_t byteCount, bool isWrite);
    int DcacheAllocAll(long n);
    void DcacheRelocateInit();

    static inline bool *dcacheValid; /* is the cached block valid? */
    static inline off64_t  *dcacheBlk; /* which block it cached */
    static inline uint64_t *dcacheLastused; /* last used ticks for cache entries */
    static inline char *dcacheBuf; /* cached block data */
    static inline uint64_t dcacheUsetick; /* current use tick */
    static inline uint64_t dcacheRaccess;
    static inline uint64_t dcacheRhit;
    static inline uint64_t dcacheRmiss;
    static inline uint64_t dcacheRreplace;
    static inline bool dcacheExitRegistered = false;
    static inline bool dcacheInitialized = false;

    /*
    *  Shadow config:
    *
    *  Active set of the configurations.
    *  Global configuration 'dcacheConfig' will be transferred here when
    *  when DcacheInit() is called
    */
    static inline DevCacheConfig dcacheConfig = {0, 16, 1};

    static inline long dcacheRelocateOffset0[] = {
        20, -20, 40, -40, 80, -80, 160, -160,
        320, -320, 640, -640, 1280, -1280, 2560, -2560,
    };
    static inline int dcacheRelocateOffset[16];
};

#ifdef HAVE_SPARSE_SPARSE_H
    int SparseImportSegment(const void *data, int len, unsigned int block, unsigned int nrBlock);
#endif

class HmfsIo {
public:
    int DevRead(void *buf, uint64_t offset, size_t len);
    int DevReadAhead(uint64_t offset);
    int DevWrite(void *buf, uint64_t offset, size_t len);
    int DevWriteBlock(void *buf, uint64_t blkAddr);
    int DevWriteDump(void *buf, uint64_t offset, size_t len);

    /* All bytes in the buffer must be 0 use DevFill(). */
    int DevFill(void *buf, uint64_t offset, size_t len);
    int DevFillBlock(void *buf, uint64_t blkAddr);
    int DevReadBlock(void *buf, uint64_t blkAddr);
    int DevReadaBlock(uint64_t blkAddr);
    int DevReadVersion(void *buf, uint64_t offset, size_t len);

#ifdef HAVE_SPARSE_SPARSE_H
    int SparseReadBlk(uint64_t block, int count, void *buf);
    int SparseWriteBlk(uint64_t block, int count, const void *buf);
    int SparseWriteZeroedBlk(uint64_t block, int count);
    int SparseMergeBlocks(uint64_t start, uint64_t num, int zero);

#else
    int SparseReadBlk(uint64_t block, int count, void *buf);
    int SparseWriteBlk(uint64_t block, int count, const void *buf);
    int SparseWriteZeroedBlk(uint64_t block, int count);
#endif
    int HmfsFsyncDevice();
    int HmfsInitSparseFile();
    void HmfsReleaseSparseResource();
    int HmfsFinalizeDevice();

    static void CreateInstance(CmdConfig &cfgPara);
    static HmfsIo& GetInstance();
    HmfsIo(CmdConfig& cmdPara) : cmdPara_(cmdPara) {}

private:
    static std::unique_ptr<HmfsIo> instance_;
    CmdConfig& cmdPara_;
    void HmfsFinalizeDeviceSparse(int &ret);
#ifdef HAVE_SPARSE_SPARSE_H
    char **blocks_ = nullptr;
    char *zeroedBlock = nullptr;
    struct sparse_file *sparseFile_ = nullptr;
    uint64_t blocksCount_;
#endif
};
}

#endif